/**
 * Copyright 2021 xpstem.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.xpstem.lelink;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.xpstem.lelink.bluetooth.BluetoothConnection;
import com.xpstem.lelink.component.EV3DeviceView;
import com.xpstem.lelink.connection.BrickConnection;
import com.xpstem.lelink.device.EV3InputDevice;
import com.xpstem.lelink.device.EV3OutputDevice;
import com.xpstem.lelink.device.EV3Sensor;
import com.xpstem.lelink.device.EV3ServoMotor;
import com.xpstem.lelink.enums.Brake;
import com.xpstem.lelink.enums.InputPort;
import com.xpstem.lelink.enums.Layer;
import com.xpstem.lelink.enums.OutputPort;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author billy zhang
 */
public class BasicActivity extends AppCompatActivity {

    public static final String TAG = BasicActivity.class.getSimpleName();

    private BrickConnection brickConnection;
    private EV3BrickClient brickClient;
    private String macAddress;
    private Map<InputPort, ScheduledExecutorService> schServiceMap;
    private ExecutorService cmdExecutor;
    private int retryCount = 3;
    private Map<Object, EV3DeviceView> deviceViewMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_basic);

        schServiceMap = new HashMap<>();
        deviceViewMap = new HashMap<>();

        deviceViewMap.put(InputPort.P1, (EV3DeviceView) findViewById(R.id.port_1));
        deviceViewMap.put(InputPort.P2, (EV3DeviceView) findViewById(R.id.port_2));
        deviceViewMap.put(InputPort.P3, (EV3DeviceView) findViewById(R.id.port_3));
        deviceViewMap.put(InputPort.P4, (EV3DeviceView) findViewById(R.id.port_4));
        deviceViewMap.put(InputPort.A, (EV3DeviceView) findViewById(R.id.port_a));
        deviceViewMap.put(InputPort.B, (EV3DeviceView) findViewById(R.id.port_b));
        deviceViewMap.put(InputPort.C, (EV3DeviceView) findViewById(R.id.port_c));
        deviceViewMap.put(InputPort.D, (EV3DeviceView) findViewById(R.id.port_d));

        deviceViewMap.put(OutputPort.A, (EV3DeviceView) findViewById(R.id.port_a));
        deviceViewMap.put(OutputPort.B, (EV3DeviceView) findViewById(R.id.port_b));
        deviceViewMap.put(OutputPort.C, (EV3DeviceView) findViewById(R.id.port_c));
        deviceViewMap.put(OutputPort.D, (EV3DeviceView) findViewById(R.id.port_d));

        cmdExecutor = Executors.newCachedThreadPool();

        macAddress = getIntent().getStringExtra("macAddress");

        brickClient = new EV3BrickClient(Layer.L0);
        brickClient.setDeviceListener(new EV3BrickClient.DeviceListener() {
            @Override
            public void onValueChanged(InputPort inputPort, EV3Sensor ev3Sensor) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // 处理传感器数据。
                        deviceViewMap.get(inputPort).update(ev3Sensor);
                    }
                });
            }

            @Override
            public void onLink(InputPort port, EV3InputDevice inputDevice) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        deviceViewMap.get(port).linkDevice(inputDevice);
                    }
                });

                if (!(inputDevice instanceof EV3Sensor)) {
                    return;
                }

                if (schServiceMap.containsKey(port)) {
                    schServiceMap.get(port).shutdown();
                    schServiceMap.remove(port);
                    Log.i(TAG, "remove schedule task for port " + port);
                }

                EV3Sensor sensor = (EV3Sensor)inputDevice;

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // 处理传感器数据。
                        deviceViewMap.get(port).update(sensor);
                    }
                });

                ScheduledExecutorService schService = Executors.newScheduledThreadPool(8);
                schService.scheduleAtFixedRate(new SensorMonitor(sensor), 1, 1, TimeUnit.SECONDS);
                schServiceMap.put(port, schService);

                Log.i(TAG, "schedule readdata task for port " + port);
            }

            @Override
            public void onLost(InputPort port, EV3InputDevice inputDevice) {
                if (schServiceMap.containsKey(port)) {
                    schServiceMap.get(port).shutdown();
                    schServiceMap.remove(port);
                    Log.i(TAG, "remove schedule task for port " + port);

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            deviceViewMap.get(port).unlinkDevice();
                        }
                    });
                }
            }

            @Override
            public void onLink(OutputPort port, EV3OutputDevice outputDevice) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        deviceViewMap.get(port).linkDevice(outputDevice);
                    }
                });

            }

            @Override
            public void onLost(OutputPort port, EV3OutputDevice outputDevice) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        deviceViewMap.get(port).unlinkDevice();
                    }
                });
            }
        });

        brickConnection = new BrickConnection(this);
        brickConnection.setBtAddress(macAddress);
        brickConnection.setOnConnectListener(new BluetoothConnection.OnConnectListener() {

            @Override
            public void onConnectSuccess() {
                brickClient.connect(brickConnection);
            }

            @Override
            public void onConnectFailed() {
                retryCount--;
                if (retryCount >0 ) {
                    brickConnection.connect();
                }
            }

            @Override
            public void onConnectionLost(Exception ex) {

            }
        });
        brickConnection.connect();

        findViewById(R.id.startBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                cmdExecutor.submit(new Runnable() {
                    @Override
                    public void run() {
                        EV3ServoMotor motor = (EV3ServoMotor) brickClient.getOutputDevice(OutputPort.A);
                        try {
                            motor.startOnPower(50);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });

            }
        });

        findViewById(R.id.stopBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                cmdExecutor.submit(new Runnable() {
                    @Override
                    public void run() {
                        EV3ServoMotor motor = (EV3ServoMotor) brickClient.getOutputDevice(OutputPort.A);
                        try {
                            motor.stop(Brake.FLOAT);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });

            }
        });
    }

    @Override
    protected void onDestroy() {
        if (schServiceMap != null) {
            for (ScheduledExecutorService sch : schServiceMap.values()) {
                sch.shutdown();
            }
        }
        if (brickConnection != null) {
            try {
                brickConnection.disconnect();
                brickConnection = null;
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        super.onDestroy();
    }

}